/*******************************************************************************
 * Copyright (c) 2011 TXT e-solutions SpA
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * This work was performed within the IoT_at_Work Project
 * and partially funded by the European Commission's
 * 7th Framework Programme under the research area ICT-2009.1.3
 * Internet of Things and enterprise environments.
 *
 *
 * Authors:
 *     Cristoforo Seccia (TXT e-solutions SpA)
 *
 * Contributors:
 *      Domenico Rotondi (TXT e-solutions SpA)
 *******************************************************************************/
package it.txt.access.capability.commons.utils;

import it.txt.access.capability.commons.osgi.CapabilityCommonsActivator;

import java.io.OutputStream;
import java.io.StringWriter;
import java.io.Writer;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.xml.transform.OutputKeys;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Element;
import org.w3c.dom.Node;

import com.sun.corba.se.spi.activation.Activator;

/**
 * This class logs or serializes the content of both XML Documents or Elements (DOM representation).
 * 
 * @author Cristoforo Seccia (TXT e-solutions SpA)
 * @author Salvatore Piccione (TXT e-solutions SpA - salvatore.piccione AT network.txtgroup.com)
 */
public class XMLPrinter {

    private static final Logger LOGGER = Logger.getLogger(XMLPrinter.class.getName());

    private static final String TRANSFORMER_CLASS = "org.apache.xalan.xsltc.trax.TransformerFactoryImpl";

    private static final String INDENT_PROPERTY = "indent-number";

    private static final int INDENT = 4;

    public static void logDocumentElement(Element capabilityElement, String message, boolean indent){
        LOGGER.log(Level.INFO, "\n"
                + "***********************************************\n"
                + "{0}\n{1}\n"
                + "***********************************************\n",
                new String[] {message, printDocumentElement(capabilityElement, indent)});
    }

    public static void logDocumentNode(Node capabilityNode, String message, boolean indent){
        LOGGER.log(Level.INFO, "\n"
                + "***********************************************\n"
                + "{0}\n{1}\n"
                + "***********************************************\n",
               new String[] {message, printDocumentNode(capabilityNode, indent)});
    }

    public static String printDocumentElement(Element capabilityElement, boolean indent){
        String result = "Error";
        try {
            result = printXML(capabilityElement, indent);
        } catch (TransformerConfigurationException ex) {
            LOGGER.log(Level.INFO, ex.getMessage(), ex);
        } catch (TransformerException ex) {
            LOGGER.log(Level.INFO, ex.getMessage(), ex);
        }
        return result;
    }

    public static String printDocumentNode(Node capabilityNode, boolean indent){
        String result = "Error";
        try {
            result = printXML(capabilityNode, indent);
        } catch (TransformerConfigurationException ex) {
            LOGGER.log(Level.INFO, ex.getMessage(), ex);
        } catch (TransformerException ex) {
            LOGGER.log(Level.INFO, ex.getMessage(), ex);
        }
        return result;
    }

    private static String printXML(Node element, boolean indent) throws TransformerException {
        return convertDOMIntoByteStream(element, indent, new StringWriter(), null).getWriter().toString();
    }
    
    public static StreamResult convertDOMIntoByteStream (Node element, boolean indent, Writer writer, OutputStream stream) throws TransformerException {
        Source xmlInput = new DOMSource(element);
        StreamResult xmlOutput = new StreamResult();
        if (writer != null)
            xmlOutput.setWriter(writer);
        if (stream != null)
            xmlOutput.setOutputStream(stream);

        //Java 1.5
//        ClassLoader cl = Thread.currentThread().getContextClassLoader();
        //TransformerFactory transformerFactory = TransformerFactory.newInstance(TRANSFORMER_CLASS, cl);
        
        System.setProperty(TransformerFactory.class.getName(), TRANSFORMER_CLASS);
        TransformerFactory transformerFactory;
//        try {
//            transformerFactory = (TransformerFactory) ClassLoaderUtils.loadClass(TRANSFORMER_CLASS,
//                    XMLPrinter.class, CapabilityCommonsActivator.class).newInstance();
//        } catch (Throwable e) {
//            throw new TransformerException(e);
//        }
        try {
            transformerFactory = TransformerFactory.newInstance();
        } catch (Throwable e) {
            if (LOGGER.isLoggable(Level.FINEST))
                LOGGER.log(Level.FINEST, "An error occurred while initialising the implementation of " + 
                        TransformerFactory.class, e);
            ClassLoader defaultClassLoader = Thread.currentThread().getContextClassLoader();
            if (LOGGER.isLoggable(Level.FINEST))
                LOGGER.finest("Default initialization failed. Bundle initialization...");            
            
            if (CapabilityCommonsActivator.getContextClassLoader() != null) {
                if (LOGGER.isLoggable(Level.FINEST))
                    LOGGER.finest("Using CapabilityCommonsActivator Class loader...");
                Thread.currentThread().setContextClassLoader(CapabilityCommonsActivator.getContextClassLoader());
            } else {
                if (LOGGER.isLoggable(Level.FINEST))
                    LOGGER.finest("Using XMLPrinter Class loader...");
                Thread.currentThread().setContextClassLoader(XMLPrinter.class.getClassLoader());
            }
            transformerFactory = TransformerFactory.newInstance();
            //reset the class loader
            Thread.currentThread().setContextClassLoader(defaultClassLoader);
        }
        
        if (indent)
            transformerFactory.setAttribute(INDENT_PROPERTY, INDENT);

        Transformer transformer = transformerFactory.newTransformer();
        if (indent)
            transformer.setOutputProperty(OutputKeys.INDENT, "yes");
        
        transformer.transform(xmlInput, xmlOutput);
        
        return xmlOutput;
    }
}
